package com.atguigu.tingshu.search.service.impl;

import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.CategoryFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.BaseCategoryView;

import com.atguigu.tingshu.search.service.ItemService;
import com.atguigu.tingshu.vo.album.AlbumInfoDetailVo;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class ItemServiceImpl implements ItemService {
    private final AlbumInfoFeignClient albumInfoFeignClient;

    private final CategoryFeignClient categoryFeignClient;

    private final UserInfoFeignClient userInfoFeignClient;

    private final RedissonClient redissonClient;

    private final ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    public ItemServiceImpl(AlbumInfoFeignClient albumInfoFeignClient, CategoryFeignClient categoryFeignClient, UserInfoFeignClient userInfoFeignClient, RedissonClient redissonClient, ThreadPoolExecutor threadPoolExecutor) {
        this.albumInfoFeignClient = albumInfoFeignClient;
        this.categoryFeignClient = categoryFeignClient;
        this.userInfoFeignClient = userInfoFeignClient;
        this.redissonClient = redissonClient;
        this.threadPoolExecutor = threadPoolExecutor;
    }

    /**
     * @param albumId 专辑id
     * @return AlbumInfoDetailVo 专辑详情数据
     * @description: 根据专辑id查询专辑详情
     * @author Petrichor
     * @date 2024/1/1 13:55
     **/
    @Override
    public AlbumInfoDetailVo findAlbumInfoDetail(Long albumId) {

        AlbumInfoDetailVo albumInfoDetailVo = new AlbumInfoDetailVo();

        // 优先查询布隆过滤器
        RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
        if (!bloomFilter.contains(albumId)) {
            log.info("布隆过滤器判断该数据不存在，直接返回属性值为空的对象...");
            return albumInfoDetailVo;
        }
        // 根据专辑id查询专辑的基本数据
        CompletableFuture<AlbumInfo> cf1 = CompletableFuture.supplyAsync(() -> {
            Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(albumId);
            AlbumInfo albumInfo = albumInfoResult.getData();
            albumInfoDetailVo.setAlbumInfo(albumInfo);
            return albumInfo;
        },threadPoolExecutor);
        // 根据专辑id查询专辑的统计数据
        CompletableFuture<Void> cf2 = CompletableFuture.runAsync(() -> {
            Result<AlbumStatVo> albumStatVoResult = albumInfoFeignClient.findAlbumStatByAlbumId(albumId);
            AlbumStatVo albumStatVo = albumStatVoResult.getData();
            albumInfoDetailVo.setAlbumStatVo(albumStatVo);
        },threadPoolExecutor);
        // 根据专辑的三级分类数据分类数据
        CompletableFuture<Void> cf3 = cf1.thenAcceptAsync(albumInfo -> {
            Result<BaseCategoryView> categoryViewResult = categoryFeignClient.findCategoryByC3Id(albumInfo.getCategory3Id());
            BaseCategoryView baseCategoryView = categoryViewResult.getData();
            albumInfoDetailVo.setBaseCategoryView(baseCategoryView);
        },threadPoolExecutor);
        // 根据用户的id查询用户数据
        CompletableFuture<Void> cf4 = cf1.thenAcceptAsync(albumInfo -> {
            Result<UserInfo> userInfoResult = userInfoFeignClient.findUserInfoByUserId(albumInfo.getUserId());
            UserInfo userInfo = userInfoResult.getData();
            UserInfoVo userInfoVo = new UserInfoVo();
            BeanUtils.copyProperties(userInfo, userInfoVo);
            albumInfoDetailVo.setAnnouncer(userInfoVo);
        },threadPoolExecutor);
        // 四个线程都完成后 输出结果
        CompletableFuture.allOf(cf1, cf2, cf3, cf4).join();
        // 返回
        return albumInfoDetailVo;
    }
}
/**
 *知识点回顾:
 * 创建线程的四种方式：
 *      *  1、继承Thread类
 *      *  2、实现Runnable接口
 *      *  3、实现Callable接口
 *      *  4、使用线程池
 *      *      方式一：使用Executors中的静态方法创建线程池
 *      *              ExecutorService threadPool1 = Executors.newCachedThreadPool();
 *      *              ExecutorService threadPool2 = Executors.newFixedThreadPool(4);
 *      *              ExecutorService threadPool3 = Executors.newSingleThreadExecutor();
 *      *      方式二：使用ThreadPoolExecutor创建线程池
 *      *
 *      *              public ThreadPoolExecutor(int corePoolSize,   核心线程数
 *      *                               int maximumPoolSize,         最大线程数
 *      *                               long keepAliveTime,          临时线程的最大空闲时间
 *      *                               TimeUnit unit,               时间单位
 *      *                               BlockingQueue<Runnable> workQueue, 阻塞队列
 *      *                               ThreadFactory threadFactory,  线程工程
 *      *                               RejectedExecutionHandler handler)      任务的拒绝策略
 *      *
 *      *                               AbortPolicy： 默认的任务拒绝策略，丢弃任务，并抛出异常
 *      *                               DiscardPolicy： 丢弃任务，但是不抛出异常
 *      *                               DiscardOldestPolicy：丢弃等待最久的任务， 但是不抛出异常
 *      *                               CallerRunsPolicy： 绕过线程池，通过主线程处理该任务
 *  优化方式一:通过线程池
 * @param albumId
 */
